package primitive3D;

import static rayCaster.ApplicationInterface.*;
import static helper.Util.*;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
import rayCaster.Ray;
import static java.lang.Math.*;

public class Sphere extends Primitive3D {	
	private double radius;

	public Sphere(){
		position = new Point3d(SPHERE_POSITION);
		radius = SPHERE_RADIUS;
		components = null;
		color = SPHERE_COLOR;
		name = new String("");
	}
	
	public Sphere( double radius ) throws Exception{
		this();
		if( radius <= 0 )
			throw new Exception("El radio debe ser un real positivo");
		this.radius = radius;
		double scaleFactor = radius/this.radius;
		this.scale(scaleFactor);
	}
	
	public Sphere(Point3d center, double radius) throws Exception{
		this(radius);		
		position = new Point3d(center);		
	}
	
	public void scale(double scaleFactor){
		radius *= scaleFactor;
	}
	
	@Override
	public void setPosition(Point3d otherPosition) {
		position = new Point3d(otherPosition);
	}
	
	@Override
	public Object clone(){		
		Sphere obj=null;
        try{
            obj=(Sphere)super.clone();
        }catch(CloneNotSupportedException ex){
            System.out.println("No se puede duplicar");
        }
        obj.radius = radius;       
        return obj;	    
	}
		
	@Override
	public void scaleFromReferencePosition(Point3d referencePosition, double scaleFactor) {
		// In this case the representation needs only origin and radius. This method does nothing		
		return;
	}
	
	public String toString(){
		StringBuffer str = new StringBuffer();
		
		str.append("Sphere \"" + getName() + "\"\nCenter = " + position + "\tRadius = " + radius + "\nColor = " + color);
		return str.toString();
	}
	
	public Point3d getOrigin() {
		return position;
	}
	
	public double getRadius() {
		return radius;
	}

	public double area() {
		return 4*PI*radius*radius;
	}

	// Devuelve la cantidad de puntos intersectados en el nombre de
	// la funcion y los puntos en el arreglo pts de longitud 2.  
	public int intersects(Ray ray, Point3d[] pts) throws Exception {
	//public int intersects(Sphere s, Point3d[] pts) throws Exception {
		Point3d center = position;
		Vector3d vecDir = ray.getVecDir();
		// vecCO va desde el centro de la esfera hasta el origen del rayo
		Vector3d vecCO = getVector(center, ray.getOrigin());
		
		// Se resuelve la ecuacion cuadratica de coeficientes:
		double a = vecDir.dot(vecDir);
		double b = 2*vecDir.dot(vecCO);
		double c = vecCO.dot(vecCO) - radius*radius;
		
		// El discriminante me informa si hubo interseccion o no
		double disc = b*b - 4*a*c;
		
		// Calculo los puntos de interseccion entre la esfera y el rayo
		if( disc > 0 ) {	// Hay 2 soluciones
			double k1 = (-b + sqrt(disc))/(2*a);
			double k2 = (-b - sqrt(disc))/(2*a);
			if( k1 >= 0 && k2 >= 0 ) {
				// p = origin + k*DirRayo, k >= 0
				ray.getPoint(k1, pts[0]);
				ray.getPoint(k2, pts[1]);
				return 2;
			}
			else if( k1 >=0 ){
				ray.getPoint(k1, pts[0]);
				return 1;
			}
			else if( k2 >=0 ){
				ray.getPoint(k2, pts[0]);
				return 1;
			}
			else
				return 0;
		}
		else if( disc == 0 ) {	// Hay 1 solucion
			double k = -b/(2*a);
			if( k >=0 ){
				// p = origin + k*DirRayo, k >= 0
				ray.getPoint(k, pts[0]);
				return 1;
			}
			else
				return 0;
		}
		else	// No hay soluciones reales
			return 0;
	}
}
